#include <linux/of_gpio.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <linux/uaccess.h>

#include "testled.h"

int mymajor=88;
int myminor=0;
int mydevcnt=1;

struct led{
	struct cdev cdev;
	int led2gpio;
	int led3gpio;
	int led4gpio;
	int led5gpio;
};
struct led myled;


int myled_open(struct inode* pnode,struct file* pfile){

	pfile->private_data=container_of(pnode->i_cdev,struct led,cdev);

	return 0;
}

int myled_cloes(struct inode* pnode,struct file* pfile){
	
	return 0;
}


void led_no(int ledno,struct led* pcdev){
	switch(ledno){
		case 2:
			gpio_set_value(pcdev->led2gpio,1);
			break;
		case 3:
			gpio_set_value(pcdev->led3gpio,1);
			break;
		case 4:
			gpio_set_value(pcdev->led4gpio,1);
			break;
		case 5:
			gpio_set_value(pcdev->led5gpio,1);
			break;
	}
}

void led_off(int ledno,struct led* pcdev){
	switch(ledno){
		case 2:
			gpio_set_value(pcdev->led2gpio,0);
			break;
		case 3:
			gpio_set_value(pcdev->led3gpio,0);
			break;
		case 4:
			gpio_set_value(pcdev->led4gpio,0);
			break;
		case 5:
			gpio_set_value(pcdev->led5gpio,0);
			break;
	}
	
}

long myled_ioctl(struct file* pfile,unsigned int req,unsigned long arg){
	struct led* pcdev=(struct led*)pfile->private_data;

	if(arg<2||arg>5){
		printk("ledno arg is invalid\n");
		return -1;
	}

	switch(req){
		case LED_NO:
			led_no(arg,pcdev);
			break;
		case LED_OFF:
			led_off(arg,pcdev);
			break;
	}
	return 0;
}

struct file_operations gmydevops={
	.owner = THIS_MODULE,
	.open = myled_open,
	.release = myled_cloes,
	.unlocked_ioctl = myled_ioctl,
};


void init_led(struct led* pcdev,struct device_node *pdtnode){
#if 1
	pcdev->led2gpio=of_get_named_gpio(pdtnode,"led2-gpio",0);
	pcdev->led3gpio=of_get_named_gpio(pdtnode,"led3-gpio",0);
	pcdev->led4gpio=of_get_named_gpio(pdtnode,"led4-gpio",0);
	pcdev->led5gpio=of_get_named_gpio(pdtnode,"led5-gpio",0);
/*
设备树
/ {
	fs4412-leds {
		led2-gpio = <12>;
		led3-gpio = <13>;
		led4-gpio = <14>;
		led5-gpio = <15>;
	}
}
*/
#else
	pcdev->led2gpio = of_get_named_gpio(pdtnode, "led-gpios", 0);
	pcdev->led3gpio = of_get_named_gpio(pdtnode, "led-gpios", 1);
	pcdev->led4gpio = of_get_named_gpio(pdtnode, "led-gpios", 2);
	pcdev->led5gpio = of_get_named_gpio(pdtnode, "led-gpios", 3);
/*
设备树
/ {
	fs4412-leds {
		led-gpios = <12 13 14 15>;
	}
}
*/
#endif
	gpio_request(pcdev->led2gpio,"led2");
	gpio_request(pcdev->led3gpio,"led3");
	gpio_request(pcdev->led4gpio,"led4");
	gpio_request(pcdev->led5gpio,"led5");

	gpio_direction_output(pcdev->led2gpio,0);
	gpio_direction_output(pcdev->led3gpio,0);
	gpio_direction_output(pcdev->led4gpio,0);
	gpio_direction_output(pcdev->led5gpio,0);
} 



void end_led(struct led* pcdev){
	gpio_free(pcdev->led2gpio);
	gpio_free(pcdev->led3gpio);
	gpio_free(pcdev->led4gpio);
	gpio_free(pcdev->led5gpio);
}


int __init myled_init(void)
{	
	int ret=0;

	struct device_node* pdevnode=NULL;
	pdevnode = of_find_node_by_path("/fs4412-leds");
	if(pdevnode == NULL){
		printk("get of_find_node_by_path(/fs4412-leds) failed\n");
		return -1;
	}
	dev_t devno = MKDEV(mymajor,myminor);

	ret=register_chrdev_region(devno,mydevcnt,"myled");
	if(ret){
		printk("call register_chrdev_region failed\n ");
		ret=alloc_chrdev_region(&devno,myminor,mydevcnt,"myled");
		if(ret){
			printk("call alloc_chrdev_region failed\n");
			return -1;
		}
		mymajor=MAJOR(devno);
	}
	
	cdev_init(&myled.cdev,&gmydevops);	
	myled.cdev.owner=THIS_MODULE;
	cdev_add(&myled.cdev,devno,1);
		
	init_led(&myled,pdevnode);

	return 0;
}
module_init(myled_init);

void __exit myled_exit(void)
{
	dev_t devno=MKDEV(mymajor,myminor);
	unregister_chrdev_region(devno,mydevcnt);
	cdev_del(&myled.cdev);

	end_led(&myled);
}
module_exit(myled_exit);


MODULE_LICENSE("GPL");
